﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ClosedXML;
using Plaice;
using InteropPidAuto;
using Llama;
using System.Xml.Linq;
using System.Xml;
using System.IO;
using System.Windows;
using Ingr.Yu;
using System.Data;
using System.Text.RegularExpressions;
using System.Threading;
using Ingr.SP2D;

namespace PlotDrawFromSha
{
    /// <summary>
    /// 配置文件里，symbol的实体
    /// </summary>
    public class SymInfo
    {
        /// <summary>
        /// A列，对应xml里part的类型。固定的有（PIPE FLOW OPC ST），动态的有（GATE VALVE、INS SPE、blind flange等）
        /// </summary>
        public string shortCode { get; set; }
        /// <summary>
        /// B列，pid的symbol路径，会和catalog root path组合成完整的文件路径
        /// </summary>
        public string symPath { get; set; }
        /// <summary>
        /// C列，symbol类型，因为后续根据末端、大小头、普通管件等，来进行不同的处理。
        /// </summary>
        public symType shortType { get; set; }

        /// <summary>
        /// 长度
        /// </summary>
        public double SymLen { get; set; } = 0.00762;
    }
    #region 没用到
    /// <summary>
    /// 配置文件中，symbol页的C列会对应这个
    /// </summary>
    public enum symType
    {
        管子 = 0,
        普通管件 = 1,
        大小头 = 2,
        Label = 3,
        末端 = 4,
        OPC = 5,
        流向 = 6,
        未定义 = 7
    }
    /// <summary>
    /// 图纸xml中，shortCode会出现的内容。
    /// 固定的有：PIPING FLOW OPC ST
    /// 动态的就不知道有哪些了
    /// </summary>
    //public enum ShortCode
    //{
    //    branch = 1,
    //    PIPE = 2,
    //    END = 3,
    //    CHANGE = 4,
    //    GENERAL = 5
    //}
    #endregion
    public class Plot
    {
        const string PipeLine = "Pipeline";
        /// <summary>
        /// 当前的line（itemtag）
        /// </summary>
        private string curLineName = "";
        /// <summary>
        /// 刚刚绘制的run的connector id
        /// </summary>
        private LMRepresentation curConnectorRep;
        /// <summary>
        /// 存储未匹配的元件信息，方便统一更新。用block放置后的id和tag信息
        /// </summary>
        public Dictionary<string, string> symbolInfoNotMapped = new Dictionary<string, string>();
        /// <summary>
        /// 存储绘制的OPC信息，方便统一更新。
        /// </summary>
        public Dictionary<string, string> symbolInfoOPC = new Dictionary<string, string>();
        /// <summary>
        /// 存储从配置文件中提取的symbol路径信息
        /// </summary>
        public List<SymInfo> symbolInfo = new List<SymInfo>();

        /// <summary>
        /// 存储从配置文件中提取的pid attr信息
        /// </summary>
        public Dictionary<string, string> attributeInfos = new Dictionary<string, string>();
        /// <summary>
        /// 单独存储从配置文件中提取的ItemTag信息
        /// </summary>
        public Tuple<string, string> ItemTagAttrInfo;
        /// <summary>
        /// 配置文件的路径
        /// </summary>
        private string _configPath;
        /// <summary>
        /// 配置文件的路径（默认 配置文件 - 阀门图例对应关系.xlsx）。
        /// A列（S3D阀门/末端类型）；B列（PID图例）；C列（类型）
        /// </summary>
        public string ConfigPath
        {
            get { return _configPath; }
            set
            {
                if (File.Exists(value))
                {
                    _configPath = value;
                    //initSymbolInfo();
                }
                else
                {
                    System.Windows.MessageBox.Show($"无法访问：{value}", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }

        }
        /// <summary>
        /// sql语句
        /// </summary>
        string sql;

        /// <summary>
        /// S3D解析出来的xml所在的文件夹，xml的名字届时和图纸名字相一致
        /// </summary>
        public string folderPath = "";

        /// <summary>
        /// 文件夹为true
        /// </summary>
        public bool folderOrFile = true;
        PID PIDDBConn;
        /// <summary>
        /// 图纸名字
        /// </summary>
        string curDrawingName = "SDFS1";
        /// <summary>
        /// 等待提交的数量
        /// </summary>
        int LlamaObjNeedCommit = 0;
        /// <summary>
        /// 基准点
        /// </summary>
        double XOrigin = 0.03;
        /// <summary>
        /// 基准点
        /// </summary>
        double YOrigin = 0.1;
        /// <summary>
        /// 两个管件之间的偏差值（Density x Index）
        /// </summary>
        double offsetPerUnit = 0.015;

        /// <summary>
        /// 管道label的偏移量 1.1mm
        /// </summary>
        double offsetPipeRunLabel = 0.0011;
        /// <summary>
        /// 大小头 label的偏移量 1.1mm
        /// </summary>
        double offsetChangeSizeLabel = 0.0035;
        /// <summary>
        /// 大小头两边的管子
        /// </summary>
        public Dictionary<string, string> pipesDNOnChangesPort;
        /// <summary>
        /// 记录所有创建的Pipe，为方便变径元件修改属性
        /// </summary>
        public Dictionary<string, string> AllPipeNPD = new Dictionary<string, string>();
        /// <summary>
        /// 记录所有创建的Pipe上是否已存在大小头
        /// </summary>
        public Dictionary<XElement, bool> HaveSizeChanged = new Dictionary<XElement, bool>();
        public Llama.LMADataSource DS;
        /// <summary>
        /// 图框相关的路径
        /// </summary>
        string templateRootPath = "";
        /// <summary>
        /// symbol的根目录
        /// </summary>
        public string symbolRootPath = "";
        Plaice.Placement objPlace;
        InteropPidAuto.Drawing objPIDDrawing;


        /// <summary>
        /// 构造函数
        /// </summary>
        public Plot()
        {

            DS = new LMADataSource();
            symbolRootPath = FindInfoInOptionManager("Catalog Explorer root path");
            templateRootPath = FindInfoInOptionManager("PID Template Path");
            objPlace = new Plaice.Placement();

        }

        /// <summary>
        /// 找某个options的值，比如symbol根目录
        /// </summary>
        /// <param name="OptionKey"></param>
        /// <returns></returns>
        string FindInfoInOptionManager(string OptionKey)
        {
            var objOptionSettings = new LMOptionSettings();
            objOptionSettings.Collect(DS);

            foreach (LMOptionSetting objOptionSetting in objOptionSettings)
            {
                if (objOptionSetting.Attributes["Name"].get_Value() == OptionKey)
                {
                    return objOptionSetting.Attributes["Value"].get_Value().ToString();

                }
            }
            return "Error";
        }

        /// <summary>
        /// 一个rep对象连接的2个port点。正常返回2个记录。第一个记录就是1口，第二个记录就是2口
        /// </summary>
        /// <param name="repObj"></param>
        /// <returns></returns>
        List<string> GetTwoPipesOnPorts(LMRepresentation repObj)
        {
            List<string> resIDs = new List<string>();
            //List<string> tempresIDs = new List<string>();

            //LMPipeRuns res = new LMPipeRuns();
            var relationships = repObj.Relation1Relationships;
            foreach (LMRelationship rel in relationships)
            {
                if (rel.Item1RepresentationObject.ModelItemObject != null)
                {
                    LMPipeRun pipe = DS.GetPipeRun(rel.Item1RepresentationObject.ModelItemID);
                    if (pipe != null)
                    {
                        //res.Add(pipe);
                        resIDs.Add(pipe.Id);
                    }

                }
                if (rel.Item2RepresentationObject.ModelItemObject != null)
                {
                    LMPipeRun pipe = DS.GetPipeRun(rel.Item2RepresentationObject.ModelItemID);
                    if (pipe != null)
                    {
                        //res.Add(pipe);
                        resIDs.Add(pipe.Id);
                    }
                }
            }
            relationships = repObj.Relation2Relationships;
            foreach (LMRelationship rel in relationships)
            {
                if (rel.Item1RepresentationObject.ModelItemObject != null)
                {
                    var pipe = DS.GetPipeRun(rel.Item1RepresentationObject.ModelItemID);
                    if (pipe != null)
                    {
                        //res.Add(pipe);
                        resIDs.Add(pipe.Id);
                    }

                }
                if (rel.Item2RepresentationObject.ModelItemObject != null)
                {
                    var pipe = DS.GetPipeRun(rel.Item2RepresentationObject.ModelItemID);
                    if (pipe != null)
                    {
                        //res.Add(pipe);
                        resIDs.Add(pipe.Id);
                    }
                }
            }
            //resIDs.Add(tempresIDs.Last());
            //resIDs.Add(tempresIDs.First());

            return resIDs;
        }
        /// <summary>
        /// 自遍历某个管子下的part，同时画图。管子本体事先已经通过<see cref="MyPlaceRun(double, double, double, RunOrientation, ref XElement)"/>画好了
        /// </summary>
        /// <param name="RouteElement">当前Route节点，代表里一个piperun</param>
        /// <param name="RouteCol">整个文档的Node集合</param>
        /// <param name="X">如果要画管子，管子的起始点</param>
        /// <param name="Y">如果要画管子，管子的起始点</param>
        /// <param name="orientation">当前管子节点的方向</param>
        void BuildTopology(XElement RouteElement, ref IEnumerable<XElement> RouteCol, double X4curBranch, double Y4curBranch, RunOrientation orientation)
        {
            var Layer = RouteElement.Attribute("Layer").Value.ToString();
            curLineName = RouteElement.Attribute(PipeLine).Value;
            double X = X4curBranch;
            double Y = Y4curBranch;

            double x_flow = X;//flow 的label位置
            double y_flow = Y;
            bool HasFlow = false;

            int indexOfChild = 0; //位于当前线上的第几个部件
            bool firstBranchFlg = true; //是不是当前线上的第一个分支
            XElement PreviousNodeLayerName = null; //上一个循环的节点
            int ValveBetweenTwoBranch = 0;//（用于知道两个平行分支之间的管件个数，相当于坐标偏移）
            foreach (XElement subElement in RouteElement.Elements())  //因为子节点为这样的：  <Branch ShortCode="BRANCH">1-12</Branch>，所以subElement.Value就是1-12
            {

                //子节点可能是：branch、parts（管子、阀门、cap、blind-flange等）
                if (subElement.Name.ToString().ToUpper() == Global.const_Branch)
                {
                    #region 子节点是Branch
                    XElement BranchNode = RouteCol.FirstOrDefault(x => x.Attribute("Layer").Value == subElement.Value); //居然遇到xml里有pipeline缺失的情况
                    if (BranchNode == null)
                    {
                        //2023 9 12，出现的新情况，就是因为分支可能不在xml里
                        BranchNode = new XElement(RouteElement);
                        BranchNode.RemoveAll();
                        BranchNode.SetAttributeValue("Length", offsetPerUnit);
                        BranchNode.SetAttributeValue("Width", offsetPerUnit);
                        BranchNode.SetAttributeValue("FirstOid", "");
                        BranchNode.SetAttributeValue("Layer", subElement.Value);
                        BranchNode.SetAttributeValue(PipeLine, "");
                        BranchNode.SetAttributeValue("NPD", subElement.Parent.Attribute("NPD").Value);
                    }
                    if (BranchNode != null)
                    {

                        //Plot a new branch
                        var offset = CaluOffsetBranch(RouteCol, firstBranchFlg, PreviousNodeLayerName); //上一个平行分支获得width
                        if (indexOfChild == 0)
                        {
                            offset += offsetPerUnit; // 2* offsetPerUnit;//个别情况，分支节点第一个对象直接就是branch，没有pipe
                            //2023 5 2，2个大格

                        }
                        double X_Previous = 0, Y_Previous = 0;
                        int X_offsetCount = 0, Y_offsetCount = 0;
                        int PreviousNodeWidth = 0;
                        if (PreviousNodeLayerName != null)
                        {
                            PreviousNodeWidth = (int)Math.Round(Convert.ToDouble(PreviousNodeLayerName.Attribute("Width").Value) / offsetPerUnit);
                            X_Previous = Convert.ToDouble(PreviousNodeLayerName.Attribute("X").Value);
                            Y_Previous = Convert.ToDouble(PreviousNodeLayerName.Attribute("Y").Value);
                            X_offsetCount = (int)Math.Round((X + offset - X_Previous) / offsetPerUnit);
                            Y_offsetCount = (int)Math.Round((Y + offset - Y_Previous) / offsetPerUnit);
                        }

                        if (orientation == RunOrientation.horizontal)
                        {
                            ////垂直绘制的情况
                            if (PreviousNodeLayerName != null)
                            {
                                if (PreviousNodeWidth == 1)//相当于上一个平行分支没有子分支了
                                {

                                    //是否有管件，有的话，没必要再空2个格子了。
                                    if (ValveBetweenTwoBranch > 0)
                                    {
                                        X = X - offsetPerUnit;
                                    }

                                }
                                else
                                {
                                    //感觉是取最大值（中间管件的个数、上一个分支的宽度）
                                    var max = Math.Max(ValveBetweenTwoBranch, PreviousNodeWidth);
                                    if (max <= 8)
                                    {
                                        // 8 就够了
                                        if (X_offsetCount > 8)
                                        {
                                            X = X - offsetPerUnit * (X_offsetCount - 8); //修正到8大格子
                                        }

                                    }
                                }


                            }

                            AllPipeNPD.Add(MyPlaceRun(X + offset, Y, Convert.ToDouble(BranchNode.Attribute("Length").Value), 1 - orientation, ref BranchNode), BranchNode.Attribute("NPD").Value.ToString()); //切换方向
                            HaveSizeChanged.Add(BranchNode, false);
                            X += offset;
                        }
                        else if (orientation == RunOrientation.vertical)
                        {
                            ////水平绘制的情况
                            if (PreviousNodeLayerName != null)
                            {
                                if (PreviousNodeWidth == 1)//相当于上一个平行分支没有子分支了
                                {

                                    //是否有管件，有的话，没必要再空2个格子了。
                                    if (ValveBetweenTwoBranch > 0)
                                    {
                                        Y = Y - offsetPerUnit;
                                    }

                                }
                                else
                                {
                                    //感觉是取最大值（中间管件的个数、上一个分支的宽度）
                                    var max = Math.Max(ValveBetweenTwoBranch, PreviousNodeWidth);
                                    if (max <= 8)
                                    {
                                        // 8 就够了
                                        if (Y_offsetCount > 8)
                                        {
                                            Y = Y - offsetPerUnit * (Y_offsetCount - 8); //修正到8大格子
                                        }

                                    }
                                }


                            }

                            AllPipeNPD.Add(MyPlaceRun(X, Y + offset, Convert.ToDouble(BranchNode.Attribute("Length").Value), 1 - orientation, ref BranchNode), BranchNode.Attribute("NPD").Value.ToString()); //切换方向
                            HaveSizeChanged.Add(BranchNode, false);
                            Y += offset;
                        }


                        BuildTopology(BranchNode, ref RouteCol, X, Y, 1 - orientation); //1-orientation 实现水平 竖直切换

                        if (orientation == RunOrientation.horizontal)
                        {
                            X += offsetPerUnit;
                        }
                        else if (orientation == RunOrientation.vertical)
                        {
                            Y += offsetPerUnit;
                        }


                        firstBranchFlg = false;
                        PreviousNodeLayerName = BranchNode;
                        ValveBetweenTwoBranch = 0;
                    }

                    #endregion

                }
                else
                {
                    #region 子节点是Part
                    var shotCodeInXml = subElement.Attribute("ShortCode").Value.ToUpper();
                    if (shotCodeInXml == Global.const_PipeShortCode)
                    {
                        #region PIPE
                        if (indexOfChild == 0)//Parts是pipes时，就一开始要移动下，后面没必要
                        {
                            if (orientation == RunOrientation.horizontal)
                            {
                                X += offsetPerUnit;// 2 * offsetPerUnit; //2023 5 2，2个大格子
                            }
                            else if (orientation == RunOrientation.vertical)
                            {
                                Y += offsetPerUnit;// 2 * offsetPerUnit;//2023 5 2，2个大格子
                            }
                        }
                        #endregion
                    }
                    else if (shotCodeInXml == Global.const_FlowShortCode)
                    {
                        //虽然也是放在管子上，但是放置的方法是用label，而不是symbol
                        //有个问题，label放完后，再放其他东西 会让flow的label移动。想办法放到最后
                        #region FLOW
                        ValveBetweenTwoBranch += 1;//占位，FLOW
                        //MyPlaceComp(X, Y, orientation, locFlowDirection);//FLOW
                        HasFlow = true;
                        x_flow = X;
                        y_flow = Y;
                        if (orientation == RunOrientation.horizontal)
                        {
                            X += offsetPerUnit;
                        }
                        else if (orientation == RunOrientation.vertical)
                        {
                            Y += offsetPerUnit;
                        }
                        #endregion
                    }
                    else if (shotCodeInXml == Global.const_OPCShortCode)
                    {
                        #region OPC
                        string resSymOPC = "";
                        //Yuanpeng Zhang: 如果OPC是最后一个对象，则放在末端，否则，放在起点
                        if (subElement.NextNode == null)
                        {
                            //想办法找到管子的末端（右边或者上面）
                            if (orientation == RunOrientation.horizontal)
                            {
                                resSymOPC = MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), Y, orientation, locOPC);//OPC
                            }
                            else if (orientation == RunOrientation.vertical)
                            {
                                resSymOPC = MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), orientation, locOPC);//OPC
                            }
                        }
                        else//放在起点
                        {
                            resSymOPC = MyPlaceComp(X, Y, orientation, locOPC);//OPC
                            indexOfChild--;
                            if (orientation == RunOrientation.horizontal)
                            {
                                X += offsetPerUnit;
                            }
                            else if (orientation == RunOrientation.vertical)
                            {
                                Y += offsetPerUnit;
                            }
                        }
                        objPIDDrawing.Save();//不能放下面，会让symobj为null
                        LMSymbol symObj = DS.GetSymbol(resSymOPC); //(obj.Representations.Nth[1].Id);
                        var spid = symObj.ModelItemID;
                        if (symObj != null && !string.IsNullOrEmpty(spid))
                        {
                            symbolInfoOPC.Add(spid, subElement.Value);
                        }
                        #endregion
                    }
                    else if (shotCodeInXml == Global.const_STShortCode)
                    {
                        #region 疏水阀ST（也就是一个普通管件，放这里只是为了方便和yangqi的拓扑能对应）
                        ValveBetweenTwoBranch += 1;//占位，普通管件
                        MyPlaceComp(X, Y, orientation, locST);//疏水阀

                        if (orientation == RunOrientation.horizontal)
                        {
                            X += offsetPerUnit;
                        }
                        else if (orientation == RunOrientation.vertical)
                        {
                            Y += offsetPerUnit;
                        }
                        #endregion
                    }
                    else
                    {
                        //这里的shortCode，从杨启生成xml的角度，是动态的。叫什么的都可能有
                        var symInfoInExcel = symbolInfo.FirstOrDefault(sym => sym.shortCode.ToUpper() == shotCodeInXml);
                        if (symInfoInExcel != null)
                        {
                            #region excel里的symbol 和 xml里的shrotcode能匹配上
                            switch (symInfoInExcel.shortType)
                            {
                                case symType.末端:
                                    if (subElement.NextNode == null)
                                    {
                                        #region end
                                        bool NeedPairedFlg = false;
                                        //2023 11 19，增加配对法兰
                                        if (subElement.Attributes().Any(x => x.Name.ToString().ToUpper() == "NEEDPAIREDFLANGE"))
                                        {
                                            if (subElement.Attribute("NEEDPAIREDFLANGE").Value == "1")
                                            {
                                                NeedPairedFlg = true;
                                            }
                                        }
                                        else
                                        {
                                            //没有，默认为需要
                                            NeedPairedFlg = true;
                                        }
                                        if (indexOfChild == 0 && Layer == "1")
                                        {
                                            MyPlaceComp(X4curBranch - 0.6 * offsetPerUnit, Y, RunOrientation.horizontal, symInfoInExcel.symPath, Math.PI);// 因为xml里会在母线的第一个节点搞一个end对象
                                        }
                                        //想办法找到管子的末端（右边或者上面）
                                        else if (orientation == RunOrientation.horizontal)
                                        {
                                            if (RouteElement.Attribute("jincou").Value == "1")
                                            {
                                                MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value) + symInfoInExcel.SymLen, Y, orientation, symInfoInExcel.symPath);//end
                                            }
                                            else
                                            {
                                                if (NeedPairedFlg)
                                                {
                                                    MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), Y, orientation, locPairedFlange);//第一片
                                                    MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value) + 0.0007, Y, orientation, symInfoInExcel.symPath);//第二片
                                                }
                                                else
                                                {
                                                    MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), Y, orientation, symInfoInExcel.symPath);//end
                                                }

                                            }

                                        }
                                        else if (orientation == RunOrientation.vertical)
                                        {
                                            if (RouteElement.Attribute("jincou").Value == "1")
                                            {
                                                MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value) + symInfoInExcel.SymLen, orientation, symInfoInExcel.symPath);//end
                                            }
                                            else
                                            {
                                                if (NeedPairedFlg)
                                                {
                                                    MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), orientation, locPairedFlange);//第一片
                                                    MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value) + 0.0007, orientation, symInfoInExcel.symPath);//第二片
                                                }
                                                else
                                                {
                                                    MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), orientation, symInfoInExcel.symPath);//end
                                                }

                                            }

                                        }
                                        break;
                                        #endregion
                                    }
                                    else
                                    {
                                        //虽然是末端类型，但是不是真正的最后一个元件
                                        #region 普通管件（就是放在管子上的）
                                        ValveBetweenTwoBranch += 1;//占位，普通管件

                                        if (subElement.Attributes().Any(x => x.Name == "jincou"))//倒数第二个
                                        {
                                            if (orientation == RunOrientation.horizontal)
                                            {
                                                MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), Y, orientation, symInfoInExcel.symPath);//end

                                            }
                                            else
                                            {
                                                MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), orientation, symInfoInExcel.symPath);//end

                                            }
                                        }
                                        else
                                        {
                                            MyPlaceComp(X, Y, orientation, symInfoInExcel.symPath);//普通管件
                                        }


                                        if (orientation == RunOrientation.horizontal)
                                        {
                                            X += offsetPerUnit;
                                        }
                                        else if (orientation == RunOrientation.vertical)
                                        {
                                            Y += offsetPerUnit;
                                        }
                                        break;
                                        #endregion
                                    }
                                case symType.大小头:
                                    #region 大小头
                                    ValveBetweenTwoBranch += 1;//占位，大小头
                                    #region 处理大小头的数据
                                    string[] Size = subElement.Value.Replace(" ", "").Replace("*", "X").Replace("x", "X").Split('X');
                                    //23 03 30，处理下单位，确保属性能更新进去
                                    string pattern = @"\d";
                                    Regex regex = new Regex(pattern);
                                    string uom = regex.Replace(Size.Last(), "");//最后一个是200mm，就得到mm 
                                    if (string.IsNullOrEmpty(uom))
                                    {
                                        //Yuanpeng Zhang: (默认mm)修改尺寸需要格式为：“200 mm”，一个空格，带着单位
                                        uom = "mm";//REPLACE时，不能空字符串
                                    }
                                    bool reverse = false; // 200x100mm 为常规的（false）。50x80mm为非常规的（true）。
                                    if (Convert.ToInt16(Size[0].ToString().Replace(uom, "").Replace(" ", "")) < Convert.ToInt16(Size[1].ToString().Replace(uom, "").Replace(" ", "")))
                                    {
                                        reverse = true;
                                    }
                                    #endregion
                                    string newSymId = MyPlaceComp(X, Y, orientation, symInfoInExcel.symPath);//大小头
                                    objPIDDrawing.Save();//不能放下面，会让symobj为null
                                    LMSymbol symObj = DS.GetSymbol(newSymId); //(obj.Representations.Nth[1].Id);
                                    var spid = symObj.ModelItemID;
                                    if (symObj != null)
                                    {
                                        #region 看情况反转大小头
                                        var changeObj = DS.GetPipingComp(ref spid).AsLMAItem();
                                        double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                                        objPlace.PIDConnectPointLocation(symObj, 1, ref x1, ref y1); //默认大小头的symbol是1大，2小
                                        objPlace.PIDConnectPointLocation(symObj, 2, ref x2, ref y2);
                                        if (reverse)
                                        {   //让大小头判断逻辑反一下
                                            double tempx, tempy;
                                            tempx = x1;
                                            tempy = y1;
                                            x1 = x2;
                                            y1 = y2;
                                            x2 = tempx;
                                            y2 = tempy;
                                        }
                                        var pipeIDs = GetTwoPipesOnPorts(symObj.AsLMRepresentation());
                                        LMPipeRun runMaxDN;
                                        LMPipeRun runMinDN;
                                        if (orientation == RunOrientation.horizontal)
                                        {
                                            if (x1 > x2)
                                            {
                                                Console.WriteLine("2左1右");
                                                //需要重新画一下

                                                objPlace.PIDDeleteItem(changeObj);
                                                //Yuanpeng zhang
                                                _LMAItem Run1 = (_LMAItem)DS.GetPipeRun(pipeIDs.First());
                                                _LMAItem Run2 = (_LMAItem)DS.GetPipeRun(pipeIDs.Last());
                                                objPlace.PIDJoinRuns(ref Run1, ref Run2);

                                                newSymId = MyPlaceComp(X, Y, orientation, symInfoInExcel.symPath, Math.PI);//大小头
                                                objPIDDrawing.Save();//不能放下面，会让symobj为null
                                                symObj = DS.GetSymbol(newSymId);
                                                runMinDN = DS.GetPipeRun(pipeIDs.Last());
                                                runMaxDN = DS.GetPipeRun(pipeIDs.First());
                                                var temp = Size[1];
                                                Size[1] = Size[0];
                                                Size[0] = temp;
                                                //因为delete后，线是不会重新连上，所以再放一个大小头时，需要人为接上
                                                //PlaceRunInputs RunInputs = new Plaice.PlaceRunInputs();
                                                //RunInputs.AddSymbolTarget(symObj, X, Y);
                                                //objPlace.PIDPlaceRun(runMaxDN.AsLMAItem(), RunInputs);
                                            }
                                            else
                                            {
                                                Console.WriteLine("1左2右");
                                                runMinDN = DS.GetPipeRun(pipeIDs.First());
                                                runMaxDN = DS.GetPipeRun(pipeIDs.Last());
                                            }
                                        }
                                        else
                                        {
                                            if (y1 > y2)
                                            {
                                                Console.WriteLine("1上2下");
                                                //需要重新画一下
                                                objPlace.PIDDeleteItem(changeObj);
                                                //Yuanpeng zhang
                                                _LMAItem Run1 = (_LMAItem)DS.GetPipeRun(pipeIDs.First());
                                                _LMAItem Run2 = (_LMAItem)DS.GetPipeRun(pipeIDs.Last());
                                                objPlace.PIDJoinRuns(ref Run1, ref Run2);

                                                newSymId = MyPlaceComp(X, Y, orientation, symInfoInExcel.symPath, Math.PI);//大小头
                                                objPIDDrawing.Save();//不能放下面，会让symobj为null
                                                symObj = DS.GetSymbol(newSymId);
                                                runMaxDN = DS.GetPipeRun(pipeIDs.Last());
                                                runMinDN = DS.GetPipeRun(pipeIDs.First());
                                                var temp = Size[1];
                                                Size[1] = Size[0];
                                                Size[0] = temp;


                                            }
                                            else
                                            {
                                                Console.WriteLine("2上1下");
                                                runMaxDN = DS.GetPipeRun(pipeIDs.First());
                                                runMinDN = DS.GetPipeRun(pipeIDs.Last());
                                            }
                                        }
                                        #endregion
                                        #region 为属性更新做准备
                                        bool blnContinue = false;
                                        //连续时，返回的pipeIds里，runMaxDN是下游（上一个的下游，已经存在），runMinDN是中间
                                        if (HaveSizeChanged[subElement.Parent])
                                        {
                                            #region 
                                            //Yuanpeng zhang:修改获取大小头两端pipe对应NPD的方法
                                            if (AllPipeNPD.ContainsKey(pipeIDs.First()))
                                            {
                                                string TempNPD = AllPipeNPD[pipeIDs.First()].ToString();
                                                if (TempNPD == Size[0].Replace(uom, "").Replace(" ", "") + " " + uom)
                                                {
                                                    AllPipeNPD[pipeIDs.First()] = Size[1].Replace(uom, "").Replace(" ", "") + " " + uom;
                                                }
                                                else
                                                {
                                                    AllPipeNPD[pipeIDs.First()] = Size[0].Replace(uom, "").Replace(" ", "") + " " + uom;
                                                }
                                                AllPipeNPD.Add(pipeIDs.Last(), TempNPD);

                                            }
                                            else
                                            {
                                                string TempNPD = AllPipeNPD[pipeIDs.Last()].ToString();
                                                if (TempNPD == Size[0].Replace(uom, "").Replace(" ", "") + " " + uom)
                                                {
                                                    AllPipeNPD[pipeIDs.Last()] = Size[1].Replace(uom, "").Replace(" ", "") + " " + uom;
                                                }
                                                else
                                                {
                                                    AllPipeNPD[pipeIDs.Last()] = Size[0].Replace(uom, "").Replace(" ", "") + " " + uom;
                                                }
                                                AllPipeNPD.Add(pipeIDs.First(), TempNPD);
                                            }
                                            if (pipesDNOnChangesPort.ContainsKey(pipeIDs.First()))
                                            {
                                                pipesDNOnChangesPort[pipeIDs.First()] = AllPipeNPD[pipeIDs.First()];

                                            }
                                            else
                                            {
                                                pipesDNOnChangesPort.Add(pipeIDs.First(), AllPipeNPD[pipeIDs.First()]);
                                            }
                                            if (pipesDNOnChangesPort.ContainsKey(pipeIDs.Last()))
                                            {
                                                pipesDNOnChangesPort[pipeIDs.Last()] = AllPipeNPD[pipeIDs.Last()];

                                            }
                                            else
                                            {
                                                pipesDNOnChangesPort.Add(pipeIDs.Last(), AllPipeNPD[pipeIDs.Last()]);
                                            }
                                            #endregion
                                        }
                                        else
                                        {
                                            #region 
                                            //Yuanpeng zhang:修改获取大小头两端pipe对应NPD的方法
                                            if (AllPipeNPD.ContainsKey(pipeIDs.First()))
                                            {
                                                string TempNPD = AllPipeNPD[pipeIDs.First()].ToString();
                                                if (TempNPD == Size[0].Replace(uom, "").Replace(" ", "") + " " + uom)
                                                {
                                                    AllPipeNPD.Add(pipeIDs.Last(), Size[1].Replace(uom, "").Replace(" ", "") + " " + uom);
                                                }
                                                else
                                                {
                                                    AllPipeNPD.Add(pipeIDs.Last(), Size[0].Replace(uom, "").Replace(" ", "") + " " + uom);
                                                }
                                            }
                                            else
                                            {
                                                string TempNPD = AllPipeNPD[pipeIDs.Last()].ToString();
                                                if (TempNPD == Size[0].Replace(uom, "").Replace(" ", "") + " " + uom)
                                                {
                                                    AllPipeNPD.Add(pipeIDs.First(), Size[1].Replace(uom, "").Replace(" ", "") + " " + uom);
                                                }
                                                else
                                                {
                                                    AllPipeNPD.Add(pipeIDs.First(), Size[0].Replace(uom, "").Replace(" ", "") + " " + uom);
                                                }
                                            }
                                            if (pipesDNOnChangesPort.ContainsKey(pipeIDs.First()))
                                            {
                                                pipesDNOnChangesPort[pipeIDs.First()] = AllPipeNPD[pipeIDs.First()];

                                            }
                                            else
                                            {
                                                pipesDNOnChangesPort.Add(pipeIDs.First(), AllPipeNPD[pipeIDs.First()]);
                                            }
                                            if (pipesDNOnChangesPort.ContainsKey(pipeIDs.Last()))
                                            {
                                                pipesDNOnChangesPort[pipeIDs.Last()] = AllPipeNPD[pipeIDs.Last()];

                                            }
                                            else
                                            {
                                                pipesDNOnChangesPort.Add(pipeIDs.Last(), AllPipeNPD[pipeIDs.Last()]);
                                            }
                                            HaveSizeChanged[subElement.Parent] = true;
                                            #endregion
                                        }

                                        //if (runMaxDN != null)
                                        //{
                                        //    //DS.BeginTransaction();
                                        //    //runMaxDN.Attributes["NominalDiameter"].set_Value(Size[0]);
                                        //    //runMaxDN.Commit();   在这里修改属性和commit，会导致后面出现未知的错误。。。所以用一个字典先存起来，等画完后再去更新属性。
                                        //    //DS.CommitTransaction();
                                        //    if (!pipesDNOnChangesPort.ContainsKey(runMaxDN.Id))
                                        //    {
                                        //        pipesDNOnChangesPort.Add(runMaxDN.Id, Size[0].Replace(uom, "").Replace(" ", "") + " " + uom);//有200 变为 200 mm
                                        //    }
                                        //    else
                                        //    {
                                        //        blnContinue = true;
                                        //        pipesDNOnChangesPort[runMaxDN.Id] = Size[1].Replace(uom, "").Replace(" ", "") + " " + uom;
                                        //        pipesDNOnChangesPort.Add(runMinDN.Id, Size[0].Replace(uom, "").Replace(" ", "") + " " + uom);//有200 变为 200 mm
                                        //        Console.WriteLine("连续的大小头。上一个小头的管子，同样是下一个连续大小头的小头。而中间的id是新的，作为上一个的新的小头和下一个的大头");
                                        //    }
                                        //}
                                        //////spid = dtChanges.Select($"item1location = '2'").First()["sp_modelitemid"].ToString();
                                        ////LMPipeRun run2 = DS.GetPipeRun(spid);  //.Connect2Connectors.Nth[1].ModelItemID);
                                        //if (runMinDN != null)
                                        //{
                                        //    //DS.BeginTransaction();
                                        //    //runMinDN.Attributes["NominalDiameter"].set_Value(Size[1]);
                                        //    //runMinDN.Commit();
                                        //    //DS.CommitTransaction();

                                        //    if (!pipesDNOnChangesPort.ContainsKey(runMinDN.Id))
                                        //    {
                                        //        pipesDNOnChangesPort.Add(runMinDN.Id, Size[1].Replace(uom, "").Replace(" ", "") + " " + uom);//有200mm 变为 200 mm
                                        //    }
                                        //    else
                                        //    {
                                        //        blnContinue = true;
                                        //        pipesDNOnChangesPort[runMinDN.Id]= Size[1].Replace(uom, "").Replace(" ", "") + " " + uom;//有200mm 变为 200 mm
                                        //        //Console.WriteLine("连续的大小头。上一个小头的管子，同样是下一个连续大小头的小头。而中间的id是新的，作为上一个的新的小头和下一个的大头");
                                        //    }
                                        //}
                                        #endregion
                                        #region LABEL 
                                        var newRep = symObj.AsLMRepresentation();
                                        //place Label
                                        var labelPoint = new double[5];

                                        if (orientation == RunOrientation.horizontal)
                                        {
                                            labelPoint[0] = X;
                                            labelPoint[1] = X;
                                            labelPoint[2] = Y;
                                            labelPoint[3] = X;
                                            labelPoint[4] = Y - offsetChangeSizeLabel - (blnContinue ? offsetChangeSizeLabel : 0);//往下
                                            objPlace.PIDPlaceLabel(locChangeSizeLabel, labelPoint, LabeledItem: newRep, IsLeaderVisible: _Need引线);//大小头
                                        }
                                        else
                                        {
                                            labelPoint[0] = X;
                                            labelPoint[1] = X;
                                            labelPoint[2] = Y;
                                            labelPoint[3] = X + offsetChangeSizeLabel + (blnContinue ? offsetChangeSizeLabel : 0);//往右
                                            labelPoint[4] = Y;
                                            objPlace.PIDPlaceLabel(locChangeSizeLabel, labelPoint, Rotation: Math.PI / 2, LabeledItem: newRep, IsLeaderVisible: _Need引线);//大小头
                                        }
                                        #endregion



                                    }


                                    if (orientation == RunOrientation.horizontal)
                                    {
                                        X += offsetPerUnit;
                                    }
                                    else if (orientation == RunOrientation.vertical)
                                    {
                                        Y += offsetPerUnit;
                                    }
                                    break;
                                #endregion
                                case symType.普通管件:
                                    #region 普通管件（就是放在管子上的）
                                    ValveBetweenTwoBranch += 1;//占位，普通管件
                                    if (subElement.Attributes().Any(x => x.Name == "jincou"))//倒数第二个
                                    {
                                        if (orientation == RunOrientation.horizontal)
                                        {
                                            MyPlaceComp(X4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), Y, orientation, symInfoInExcel.symPath);//end

                                        }
                                        else
                                        {
                                            MyPlaceComp(X, Y4curBranch + Convert.ToDouble(RouteElement.Attribute("Length").Value), orientation, symInfoInExcel.symPath);//end

                                        }
                                    }
                                    else
                                    {
                                        MyPlaceComp(X, Y, orientation, symInfoInExcel.symPath);//普通管件
                                    }


                                    if (orientation == RunOrientation.horizontal)
                                    {
                                        X += offsetPerUnit;
                                    }
                                    else if (orientation == RunOrientation.vertical)
                                    {
                                        Y += offsetPerUnit;
                                    }
                                    break;
                                    #endregion
                            }
                            #endregion
                        }
                        else
                        {
                            #region 匹配不上的
                            //因为从杨启生成xml的角度，是动态的。叫什么的都可能有,比如INS SPE之类

                            ValveBetweenTwoBranch += 1;//占位，普通管件
                            var symId = MyPlaceComp(X, Y, orientation, this.locNotFound);//未找到的普通管件
                            objPIDDrawing.Save();//不能放下面，会让symobj为null
                            LMSymbol symObj = DS.GetSymbol(symId); //(obj.Representations.Nth[1].Id);
                            var spid = symObj.ModelItemID;
                            if (symObj != null && !string.IsNullOrEmpty(spid))
                            {
                                symbolInfoNotMapped.Add(spid, subElement.Value);
                                //LMModelItem newObj = DS.GetModelItem(spid);
                                //if (newObj != null)
                                //{
                                //    newObj.Attributes["Description"].set_Value(subElement.Value);
                                //    newObj.Commit();
                                //}
                            }

                            if (orientation == RunOrientation.horizontal)
                            {
                                X += offsetPerUnit;
                            }
                            else if (orientation == RunOrientation.vertical)
                            {
                                Y += offsetPerUnit;
                            }
                            #endregion
                        }

                    }
                    #endregion

                }

                //2023 11 04，管子上有flow的时候，放到最后
                if (subElement.NextNode == null && HasFlow)
                {
                    var labelPoint = new double[4];

                    if (orientation == RunOrientation.horizontal)
                    {
                        labelPoint[0] = x_flow;
                        labelPoint[1] = y_flow; //往右
                        labelPoint[2] = x_flow + 0.001;
                        labelPoint[3] = y_flow;
                        objPlace.PIDPlaceLabel(locFlow, labelPoint, Rotation: Math.PI / 2, LabeledItem: curConnectorRep);//FLOW
                        //var symId = MyPlaceComp(x_flow, y_flow, orientation, this.locNotFound);//未找到的普通管件
                    }
                    else if (orientation == RunOrientation.vertical)
                    {
                        labelPoint[0] = x_flow;
                        labelPoint[1] = y_flow; //往上
                        labelPoint[2] = x_flow;
                        labelPoint[3] = y_flow + 0.001;
                        objPlace.PIDPlaceLabel(locFlow, labelPoint, LabeledItem: curConnectorRep);//FLOW
                        //var symId = MyPlaceComp(x_flow, y_flow, orientation, this.locNotFound);//未找到的普通管件
                    }

                }

                indexOfChild++;

                if (LlamaObjNeedCommit > 50)
                {
                    objPIDDrawing.Save();
                    LlamaObjNeedCommit = 0;
                }
            }

        }
        /// <summary>
        /// 绘制分支时，计算应该偏移的量
        /// 逻辑和计算宽度是基本一致的
        /// </summary>
        /// <returns></returns>
        double CaluOffsetBranch(IEnumerable<XElement> RouteCol, bool FirstNodeFlg, XElement previous)
        {

            if (FirstNodeFlg)
            {
                return 0;
            }
            else
            {
                if (previous != null)
                {
                    //double MaxLen = 0;
                    //var subBranchCol = previous.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() == "BRANCH");
                    //foreach (var branch in subBranchCol)
                    //{
                    //    var BranchNode = RouteCol.First(x => x.Attribute("Layer").Value == branch.Value);
                    //    try
                    //    {
                    //        var len = Convert.ToDouble(BranchNode.Attribute("Length").Value);
                    //        if (len > MaxLen) MaxLen = len;
                    //    }
                    //    catch (Exception)
                    //    {

                    //        return 0;
                    //    }

                    //}
                    //return MaxLen;
                    return CaluBranchWidth(previous, ref RouteCol);
                }
                return 0;
            }

        }
        /// <summary>
        /// 计算分支的基础长度。每个Part或Branch节点占一单位
        /// </summary>
        /// <param name="BranchNode"></param>
        /// <returns></returns>
        double CaluBranchLen(ref XElement BranchNode)
        {
            var count = BranchNode.Elements().Count();
            double branchLen = offsetPerUnit;
            //Yuanpeng Zhang: 长度得算上Piping，再加1（如果第一个对象不是Piping）
            branchLen = offsetPerUnit * (BranchNode.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() != Global.const_PipeShortCode).Count());
            //branchLen = offsetPerUnit * (1 + BranchNode.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() != null).Count());
            var EndShortCodesInConfig = symbolInfo.Where(x => x.shortType == symType.末端).Select(x => x.shortCode.ToUpper()).ToList();

            if (BranchNode.Elements().Where(x => EndShortCodesInConfig.Contains(x.Attribute("ShortCode").Value.ToUpper())).Count() > 0)//包括end 和 OPC
            //(BranchNode.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() == "CAP" || x.Attribute("ShortCode").Value.ToUpper() == "BLIND FLANGE").Count() > 0)
            {
                //有cap
                if (count > 2)
                {
                    XElement last1 = BranchNode.Elements().ElementAt(count - 1);//倒数第1个
                    XElement last2 = BranchNode.Elements().ElementAt(count - 2);//倒数第二个
                    XElement last3 = BranchNode.Elements().ElementAt(count - 3);//倒数第三个
                    if (last1.Attribute("ShortCode").Value.ToUpper() != Global.const_OPCShortCode &&
                        last2.Attribute("ShortCode").Value.ToUpper() != Global.const_PipeShortCode && last3.Attribute("ShortCode").Value.ToUpper() == Global.const_PipeShortCode)
                    {
                        branchLen -= offsetPerUnit;//2023 11 18,最后1个法兰需要靠着最后一个管件。有个问题，如果分支有宽度，那么有问题
                        BranchNode.SetAttributeValue("jincou", "1"); //PIPE
                        BranchNode.Elements().ElementAt(count - 2).SetAttributeValue("jincou", "1"); //SHORTCODE

                    }
                }
            }
            else if (BranchNode.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() != Global.const_OPCShortCode).Count() > 0)
            {
                //有OPC(本质上也和CAP一样)
            }
            else
            {
                //无cap
                branchLen += offsetPerUnit;//相当于后面甩空了
            }

            return branchLen;
        }
        /// 计算分支的宽度,就是其所有分支中的最大长度。默认最低返回1个单位
        /// </summary>
        /// <param name="BranchNode"></param>
        /// <returns>要么没有分支（返回1），要么有分支（返回7或者更大）</returns>
        double CaluBranchWidth(XElement route, ref IEnumerable<XElement> RouteCol)
        {

            double branchWidth = 0;
            foreach (var Branch in route.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() == Global.const_Branch))
            {
                var BranchRoute = RouteCol.FirstOrDefault(x => x.Attribute("Layer").Value.ToUpper() == Branch.Value.ToUpper());
                if (BranchRoute != null)
                {
                    var lenBranch = Convert.ToDouble(BranchRoute.Attribute("Length").Value);
                    if (lenBranch > branchWidth)
                    {
                        branchWidth = lenBranch;
                    }
                }
                else
                {
                    //2023 11 04 ,branch节点没有的情况
                    var lenBranch = offsetPerUnit;
                    if (lenBranch > branchWidth)
                    {
                        branchWidth = lenBranch;
                    }
                }
            }

            ////2023 05 02,如果有宽度，就给8个单位
            if (branchWidth > 0)
            {
                if (branchWidth <= 7 * offsetPerUnit) //绘制时，会自动再加一个单位，所以给7就行
                {
                    branchWidth = 7 * offsetPerUnit; //有分支，但是小于8个单位时，给8个，大于时，按实际的来
                }
            }
            else
            {
                branchWidth = 1 * offsetPerUnit;//绘制时，会自动再加一个单位，所以给1就行。相当于客户要求的2个单位
            }
            return branchWidth;
        }
        /// <summary>
        /// 绘图模式
        /// </summary>
        public enum PlotMode
        {
            /// <summary>
            /// 在已有的同名文件中
            /// </summary>
            OnExisted = 0,
            /// <summary>
            /// 新建一个图纸（后缀from3d）
            /// </summary>
            New = 1
        }

        /// <summary>
        /// 标注是否带引线
        /// </summary>
        bool _Need引线;

        /// <summary>
        /// end of group。配置文件中必须指定。
        /// </summary>
        public string locEndOfGroup = "Piping\\Segment Breaks\\End of Group.sym";
        /// <summary>
        /// 管道。配置文件中必须指定。
        /// </summary>
        public string locMainRun = "Piping\\Routing\\Process Lines\\Primary Piping.sym";
        /// <summary>
        /// 管道Label。配置文件中必须指定。
        /// </summary>
        public string locMainRunLabel = "Piping\\Labels - Piping Segments\\Line Number.sym";
        /// <summary>
        /// 大小头 Label。配置文件中必须指定。
        /// </summary>
        public string locChangeSizeLabel = "Piping\\Labels - Piping Components\\Red Nominal Diameter.sym";
        /// <summary>
        /// OPC。配置文件中必须指定。
        /// </summary>
        public string locOPC = "Piping\\Piping OPC's\\Off-Drawing.sym";
        /// <summary>
        /// 流向。配置文件中必须指定。
        /// </summary>
        public string locFlow = "Piping\\Labels - Piping Segments\\Flow Direction.sym";
        /// <summary>
        /// 疏水阀。配置文件中必须指定。
        /// </summary>
        public string locST = "";
        /// <summary>
        /// 未找到的shortCode会用的symbol。配置文件中必须指定。
        /// </summary>
        public string locNotFound = "";
        /// <summary>
        /// 绘制配对法兰会用的symbol。配置文件中必须指定。
        /// </summary>
        public string locPairedFlange = "";
        /// <summary>
        /// 开始画图
        /// </summary>
        public void StartPlot(PlotMode plotMode, double offsetPerUnit, bool appVisible, bool Need引线, PID pIDDBConn)
        {
            this._Need引线 = Need引线;
            this.PIDDBConn = pIDDBConn;
            this.offsetPerUnit = offsetPerUnit;
            #region 打开exe
            System.Diagnostics.Process[] ps = System.Diagnostics.Process.GetProcessesByName("draft");
            if (ps.Length > 0)
            {
                //还没想好
                //MessageBox.Show("请关闭现有的sha图纸，或者在任务管理器中结束draft进程!", "图纸未关闭");
                //return;
                foreach (var pid in ps)
                {
                    pid.Kill();
                }
            }
            InteropPidAuto.Application objPIDAutoAPP;
            objPIDAutoAPP = new InteropPidAuto.Application
            {
                Visible = appVisible
            }; //此时会开启一个空白的draft进程
            objPIDAutoAPP.Activate();
            #endregion

            sql = $"select * from xxxSchemaName.t_plantgroup t";
            DataTable dtPBS = new DataTable();
            PIDDBConn.PIDReadDataByDBA(PIDDBConn.PIDPlant_DBNAME, PIDDBConn.PIDPlant_User, sql, out dtPBS);
            //Log.Writelog($"查询PBS结束。", "StartPlot", false);
            sql = $"select * from xxxSchemaName.t_drawing t";
            DataTable dt = new DataTable();
            var res = PIDDBConn.PIDReadDataByDBA(PIDDBConn.PIDPlant_DBNAME, PIDDBConn.ActivePlantPIDApp_User, sql, out dt);
            if (dt != null)
            {
                //Log.Writelog($"查询图纸结束。", "StartPlot", false);
            }
            else
            {
                MessageBox.Show("查询图纸有误。" + res);
                return;
            }
            List<FileInfo> XMLS = new List<FileInfo>();
            if (folderOrFile == false)
            {
                if (System.IO.File.Exists(folderPath)) //这时候带.xml
                {
                    XMLS.Add(new FileInfo(folderPath));
                }
                else
                {
                    MessageBox.Show($"选择的xml文件是无效的:{folderPath}");
                    return;
                }
            }
            else
            {
                if (System.IO.Directory.Exists(folderPath))
                {
                    var xmlFolder = new DirectoryInfo(folderPath);
                    //Log.Writelog($"准备绘制，目录{folderPath}下，共{xmlFolder.EnumerateFiles("*.xml").Count().ToString()}个xml文件", "StartPlot", false);

                    foreach (FileInfo FILE in xmlFolder.EnumerateFiles("*.xml"))
                    {
                        XMLS.Add(FILE);
                    }
                }
                else
                {
                    MessageBox.Show($"选择的xml文件夹是无效的:{folderPath}");
                    return;
                }
            }



            foreach (FileInfo xmlFile in XMLS)//
            {
                #region 每个图纸关闭后，都要独立做一次这个，不然就application error了
                objPIDAutoAPP = new InteropPidAuto.Application
                {
                    Visible = appVisible
                };
                objPIDAutoAPP.Activate();
                #endregion
                #region 拿到图纸
                var curDwgInfo = dt.AsEnumerable().FirstOrDefault(x => x["name"].ToString() == xmlFile.Name.Replace(".xml", ""));
                string strPlantGroupName = ""; //新建图纸时会用到
                string strTemplateFileName = "";//新建图纸时会用到
                if (curDwgInfo == null)
                {
                    //Log.Writelog($"未在当前项目中找到图纸 {xmlFile.Name}。请前往Drawing Manager中创建名为【{xmlFile.Name}】图纸先。。跳过该xml。", "StartPlot", false);
                    //MessageBox.Show($"未在当前项目中找到图纸 {xmlFile.Name}。\r\n请前往Drawing Manager中创建名为【{xmlFile.Name}】图纸先。。跳过该xml。");
                    continue;
                }
                else
                {

                    var pbsInfo = dtPBS.AsEnumerable().FirstOrDefault(x => x["SP_ID"].ToString() == curDwgInfo["sp_plantgroupid"].ToString());
                    strPlantGroupName = pbsInfo["Name"].ToString();
                    strTemplateFileName = templateRootPath + "\\" + curDwgInfo["template"].ToString();

                }
                if (plotMode == PlotMode.New)
                {

                    curDrawingName = $"{xmlFile.Name.Replace(".xml", "")}-{"From3D"}";
                    var dwgWillCreate = dt.AsEnumerable().FirstOrDefault(x => x["name"].ToString() == curDrawingName);
                    if (dwgWillCreate != null)
                    {
                        //如果该模式下的图纸已经存在，就不需要再新建了
                    }
                    else
                    {
                        if (File.Exists(strTemplateFileName))
                        {
                            objPIDAutoAPP.Drawings.Add(strPlantGroupName, ref strTemplateFileName, curDrawingName, ref curDrawingName);
                        }
                        else
                        {
                            continue;
                        }
                    }

                }
                else if (plotMode == PlotMode.OnExisted)
                {
                    // 原本图纸，本身就不需要新建
                    curDrawingName = xmlFile.Name.Replace(".xml", "");
                }
                #endregion

                #region 打开图纸（打开图纸后，又有一个名为smartplant的进程，算上之前空白的draft进程，就有2个）
                try
                {
                    if (objPIDAutoAPP.ActiveDrawing.Name.ToUpper().Replace(".PID", "") == curDrawingName.ToUpper())
                    {
                        //如果是自己就没必要再开了
                        objPIDDrawing = objPIDAutoAPP.ActiveDrawing;
                    }
                    else
                    {
                        objPIDDrawing = objPIDAutoAPP.Drawings.OpenDrawing(curDrawingName);
                    }
                }
                catch (Exception)//第一个图纸完成后，关闭图纸。第二个图纸进来时，就会报错
                {

                    objPIDDrawing = objPIDAutoAPP.Drawings.OpenDrawing(curDrawingName);
                }
                //Log.Writelog($"打开图纸 {curDrawingName}。", "StartPlot", false);
                #endregion


                #region 开始绘制
                if (objPIDDrawing != null)
                {
                    try
                    {

                        symbolInfoNotMapped.Clear();
                        pipesDNOnChangesPort = new Dictionary<string, string>();
                        if (System.IO.File.Exists(xmlFile.FullName))
                        {
                            //Log.Writelog($"准备读取xml： {xmlFile.FullName}。", "StartPlot", false);
                            var xDoc = XDocument.Load(xmlFile.FullName);
                            if (xDoc != null)
                            {
                                var xRoot = xDoc.Root;
                                IEnumerable<XElement> RouteCol = xRoot.Elements("Route");//每个xml里的所有piperun

                                XElement PipeRunRoot = RouteCol.First(x => x.Attribute("Layer").Value == "1");//每个xml里的母线

                                if (PipeRunRoot != null)
                                {
                                    curLineName = PipeRunRoot.Attribute(PipeLine).Value;
                                    //Log.Writelog($"找到Layer = 1。PipeLine = {curLineName}", "StartPlot", false);
                                    XOrigin = 6 * offsetPerUnit;//恢复x基准点
                                    YOrigin = 2 * offsetPerUnit;//恢复y基准点
                                    //Log.Writelog($"开始GetMaxLevel。", "StartPlot", false);
                                    GetMaxLevel(ref RouteCol);
                                    //Log.Writelog($"结束GetMaxLevel。", "StartPlot", false);
                                    var RootRunLength = PipeRunRoot.Attribute("Length").Value.ToString();

                                    //LMRepresentation xxx;
                                    //Log.Writelog($"开始母线绘制。", "StartPlot", false);
                                    var RootRunID = MyPlaceRun(XOrigin - offsetPerUnit, YOrigin, Convert.ToDouble(RootRunLength) + offsetPerUnit, RunOrientation.horizontal, ref PipeRunRoot);//画母线
                                    //Log.Writelog($"结束母线绘制。", "StartPlot", false);
                                    AllPipeNPD.Add(RootRunID, PipeRunRoot.Attribute("NPD").Value.ToString());//根管子
                                    HaveSizeChanged.Add(PipeRunRoot, false);
                                    BuildTopology(PipeRunRoot, ref RouteCol, XOrigin, YOrigin, RunOrientation.horizontal);


                                }
                                else
                                {

                                }

                            }
                            else
                            {
                                //Log.Writelog($"读取xml有误： {xmlFile.FullName}。", "StartPlot", false);
                            }
                        }

                        objPIDDrawing.Save();


                        Thread.Sleep(5000);
                        DS = new LMADataSource();
                        #region 统一去更新一些属性
                        DS.BeginTransaction();
                        DS.set_PropagateChanges(true);
                        foreach (var item in pipesDNOnChangesPort)
                        {
                            try
                            {
                                LMPipeRun pipe = DS.GetPipeRun(item.Key);
                                if (pipe != null)
                                {
                                    var DN = item.Value.Replace(" in", "\"").Replace(" In", "\"").Replace(" IN", "\"");
                                    pipe.Attributes["NominalDiameter"].set_Value(DN);
                                    pipe.Commit();
                                    string NPDAfterUpdate = "";
                                    try
                                    {
                                        NPDAfterUpdate = pipe.Attributes["NominalDiameter"].get_Value();
                                    }
                                    catch (Exception)
                                    {
                                        NPDAfterUpdate = "";
                                    }

                                    if (string.IsNullOrEmpty(NPDAfterUpdate) || NPDAfterUpdate != DN)
                                    {
                                        var doubleInNPD = ExtractNumber(DN);
                                        DN = ConvertToFraction(Convert.ToDouble(doubleInNPD)) + DN.Replace(doubleInNPD, "");
                                        pipe.Attributes["NominalDiameter"].set_Value(DN);
                                        pipe.Commit();
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }

                        }
                        foreach (var symNotFound in symbolInfoNotMapped)
                        {
                            LMModelItem obj = DS.GetModelItem(symNotFound.Key);
                            if (obj != null)
                            {
                                obj.Attributes["Description"].set_Value(symNotFound.Value);
                            }
                        }

                        foreach (var symOPC in symbolInfoOPC)
                        {
                            LMModelItem obj = DS.GetModelItem(symOPC.Key);
                            if (obj != null)
                            {
                                obj.Attributes["ToFromText"].set_Value("To " + symOPC.Value);
                            }
                        }
                        pipesDNOnChangesPort = null;
                        #endregion
                        DS.CommitTransaction();
                        objPIDDrawing.CloseDrawing(true);
                    }
                    catch (Exception E)
                    {
                        objPIDAutoAPP.Visible = true;
                        MessageBox.Show(E.Message);
                        return;
                    }

                }
                #endregion
            }

            objPIDAutoAPP.Quit();//关掉smartplant的进行
            ps = System.Diagnostics.Process.GetProcessesByName("draft");//杀掉空白的draft进行
            if (ps.Length > 0)
            {
                //还没想好
                //MessageBox.Show("请关闭现有的sha图纸，或者在任务管理器中结束draft进程!", "图纸未关闭");
                //return;
                foreach (var pid in ps)
                {
                    pid.Kill();
                }
            }

            MessageBox.Show("Done.");
        }
        /// <summary>
        /// 找到最深的分支。并且从最深分支往上遍历，每个route节点计算长度 宽度 
        /// </summary>
        /// <param name="RouteCol"></param>
        /// <returns></returns>
        public int GetMaxLevel(ref IEnumerable<XElement> RouteCol)
        {
            int res = 0; //0代表只有母线自己
            #region 深度
            foreach (XElement route in RouteCol)
            {

                var LevelNo = route.Attribute("Layer").Value;
                var len = Regex.Matches(LevelNo, "-").Count;//1-2-17-6-6-4,就是5
                if (len > res) res = len;
                route.SetAttributeValue("Level", len.ToString());
                route.SetAttributeValue("jincou", "0");
            }
            #endregion
            for (int i = res; i > -1; i--) //从最末端开始
            {
                foreach (var routeInternal in RouteCol.Where(x => x.Attribute("Level").Value == i.ToString()))
                {
                    #region width
                    var route = routeInternal;
                    var branchWidth = CaluBranchWidth(route, ref RouteCol);//宽度就是其所有分支的最大长度
                    route.SetAttributeValue("Width", branchWidth.ToString());
                    #endregion
                    #region length = 基础长度 + 额外长度
                    var branchLen = CaluBranchLen(ref route);//基础长度

                    if (i < res) //长度要重新计算 ,由于宽度为0时，CaluBranchWidth中自动给了一个1单位，所以这里从res-1改为res
                    {
                        #region 额外长度，算上每个分支的宽度
                        var allBranchs = route.Elements().Where(x => x.Attribute("ShortCode").Value.ToUpper() == Global.const_Branch);
                        foreach (var Branch in allBranchs)
                        {

                            var BranchRoute = RouteCol.FirstOrDefault(x => x.Attribute("Layer").Value.ToUpper() == Branch.Value.ToUpper());
                            if (BranchRoute != null)
                            {
                                var width = Convert.ToDouble(BranchRoute.Attribute("Width").Value);
                                //if (width == 0)
                                //{
                                //    width = 1 * offsetPerUnit; //2023 05 02,平行的流出2个大格
                                //}
                                branchLen += width;
                            }
                            else
                            {
                                //2023 11 04，主线的branch上有。但是自己独立的Route节点没有
                                branchLen += offsetPerUnit;//width最低返回1，所以再加1一个
                            }

                        }
                        if (allBranchs.Count() > 1)
                        {
                            //最后一个不需要
                            branchLen -= offsetPerUnit;
                        }
                        #endregion
                    }
                    //branchLen -= 2 * offsetPerUnit;
                    if (branchLen < 4 * offsetPerUnit)//2023 5 2，最末端长度固定为4
                    {
                        branchLen = 4 * offsetPerUnit;
                    }

                    route.SetAttributeValue("Length", branchLen.ToString());
                    #endregion

                }
            }
            return res;
        }

        /// <summary>
        /// 方向
        /// </summary>
        enum RunOrientation
        {
            /// <summary>
            ///  
            /// </summary>
            vertical = 1,
            /// <summary>
            ///  
            /// </summary>
            horizontal = 0
        }
        /// <summary>
        /// 往.PID上画线，防止Label，且赋予属性
        /// </summary>
        /// <param name="startX"></param>
        /// <param name="startY"></param>
        /// <param name="length">长度</param>
        /// <param name="orientation">方向</param>
        /// <returns>返回API创建的管子的SPID</returns>
        string MyPlaceRun(double startX, double startY, double length, RunOrientation orientation, ref XElement RunNode)
        {
            var BranchName = RunNode.Attribute(PipeLine).Value;
            //记录下每个分支的实际插入基点
            RunNode.SetAttributeValue("X", startX);
            RunNode.SetAttributeValue("Y", startY);
            #region piperun
            var RunInputs = new Plaice.PlaceRunInputs();


            if (orientation == RunOrientation.horizontal)
            {
                RunInputs.AddPoint(startX + length, startY);

            }
            else if (orientation == RunOrientation.vertical)
            {
                RunInputs.AddPoint(startX, startY + length);
            }
            if (RunNode.Attribute("Layer").Value != "1")
            {
                RunInputs.AddLocatedTarget(startX, startY);  //第一条总的run就不需要了
            }
            else
            {
                RunInputs.AddPoint(startX, startY);
            }




            var newObjStockpile = objPlace.PIDCreateItem(locMainRun);
            #endregion
            //var LayerName = RunNode.Attributes().First(a => a.Name == "Layer").Value;
            //var ItemTag = RunNode.Attributes().First(a => a.Name == "Pipeline").Value;
            //var NPD = RunNode.Attributes().First(a => a.Name == "NPD").Value;
            //var FluidCode = RunNode.Attributes().First(a => a.Name == "FluidCode").Value;
            //newObjStockpile.Attributes["Description"].set_Value(LayerName);
            //newObjStockpile.Attributes["ItemTag"].set_Value(ItemTag);
            //newObjStockpile.Attributes["NominalDiameter"].set_Value(NPD);
            //newObjStockpile.Attributes["OperFluidCode"].set_Value(FluidCode);
            #region attr，管子是因为创建item到stockpile，所以可以直接在这里修改属性
            //DS.set_PropagateChanges(false);
            foreach (XAttribute xmlAttr in RunNode.Attributes())
            {


                if (xmlAttr.Name.ToString().ToUpper() == "FLOWDIRECTION")
                {
                    switch (xmlAttr.Value.ToString().ToUpper())
                    {
                        case "UP":
                        case "RIGHT":
                            newObjStockpile.Attributes["FlowDirection"].set_Value("End 1 is downstream (Outlet)");
                            newObjStockpile.Commit();
                            break;
                        case "LEFT":
                        case "DOWN":
                            newObjStockpile.Attributes["FlowDirection"].set_Value("End 1 is upstream (Inlet)");
                            newObjStockpile.Commit();
                            break;
                        default:
                            break;
                    }
                }
                else if (attributeInfos.Any(x => x.Key.ToUpper() == xmlAttr.Name.ToString().ToUpper()))
                {

                    var attrPid = attributeInfos.First((x => x.Key.ToUpper() == xmlAttr.Name.ToString().ToUpper())).Value;
                    var valueInXml = xmlAttr.Value;
                    if (xmlAttr.Name.ToString().ToUpper() == "NPD")
                    {
                        ISPEnumeratedAttributes list = newObjStockpile.Attributes["NominalDiameter"].ISPAttribute.Attribution.ISPEnumAtts;
                        if (list != null && valueInXml.ToUpper().EndsWith("IN"))
                        {
                            valueInXml = valueInXml.Replace(" in", "\"").Replace(" In", "\"").Replace(" IN", "\"");
                            //如果没有对应的枚举，则写不进去
                        }

                        newObjStockpile.Attributes["NominalDiameter"].set_Value(valueInXml);
                        newObjStockpile.Commit();
                        string NPDAfterUpdate = "";
                        try
                        {
                            NPDAfterUpdate = newObjStockpile.Attributes["NominalDiameter"].get_Value();
                        }
                        catch (Exception)
                        {
                            NPDAfterUpdate = "";
                        }

                        if (string.IsNullOrEmpty(NPDAfterUpdate) || NPDAfterUpdate != valueInXml)
                        {
                            var doubleInNPD = ExtractNumber(valueInXml);
                            valueInXml = ConvertToFraction(Convert.ToDouble(doubleInNPD)) + valueInXml.Replace(doubleInNPD, "");
                            newObjStockpile.Attributes["NominalDiameter"].set_Value(valueInXml);
                            newObjStockpile.Commit();
                        }
                    }
                    else
                    {
                        newObjStockpile.Attributes[attrPid].set_Value(valueInXml);
                        newObjStockpile.Commit();
                    }


                }

                //newObjStockpile.Attributes[""].set_Value("");
                //newObjStockpile.Commit();


            }
            if (ItemTagAttrInfo != null)
            {
                newObjStockpile.Attributes[ItemTagAttrInfo.Item2].set_Value(RunNode.Attribute(ItemTagAttrInfo.Item1).Value);
            }

            newObjStockpile.Commit();
            #endregion
            var newConn = objPlace.PIDPlaceRun(newObjStockpile, RunInputs);

            LlamaObjNeedCommit += 1;

            #region 放置管线的label
            var newRun = DS.GetPipeRun(newObjStockpile.Id);
            var newRep = newConn.AsLMRepresentation();

            //place Label
            var labelPoint = new double[4];

            if (orientation == RunOrientation.horizontal)
            {
                labelPoint[0] = startX + length / 2;
                labelPoint[1] = startY + offsetPipeRunLabel; //往上
                labelPoint[2] = startX + length / 2;
                labelPoint[3] = startY + offsetPipeRunLabel;
                objPlace.PIDPlaceLabel(locMainRunLabel, labelPoint, LabeledItem: newRep, IsLeaderVisible: _Need引线);//管线
            }
            else
            {
                labelPoint[0] = startX - offsetPipeRunLabel; //往左
                labelPoint[1] = startY + length / 2;
                labelPoint[2] = startX - offsetPipeRunLabel;
                labelPoint[3] = startY + length / 2;
                objPlace.PIDPlaceLabel(locMainRunLabel, labelPoint, Rotation: Math.PI / 2, LabeledItem: newRep, IsLeaderVisible: _Need引线);//管线
            }
            #endregion

            #region 放置分支处的end of group
            if (curLineName == BranchName)
            {
                //2024 1 8，snec，同一个管线号，不需要加打断
                Console.WriteLine("");
            }
            else
            {
                var EndOfGroupPoint = new double[7];
                EndOfGroupPoint[0] = startX;//0是没用的
                EndOfGroupPoint[1] = startX;
                EndOfGroupPoint[2] = startY; //2个小格子  1.27 mm * 2
                EndOfGroupPoint[3] = startX + offsetPerUnit * 0.2; //4个小格子  1.27 mm * 4;
                EndOfGroupPoint[4] = startY + offsetPerUnit * 0.4; //4个小格子  1.27 mm * 4
                EndOfGroupPoint[5] = startX + offsetPerUnit * 0.4; //4个小格子  1.27 mm * 4;
                EndOfGroupPoint[6] = startY + offsetPerUnit * 0.4; //4个小格子  1.27 mm * 4
                if (RunNode.Attribute("Layer").Value != "1")
                {
                    if (orientation == RunOrientation.horizontal)
                    {
                        objPlace.PIDPlaceLabel(locEndOfGroup, EndOfGroupPoint, Rotation: Math.PI / 2, LabeledItem: newRep, IsLeaderVisible: true); //第一条总的run就不需要了
                    }
                    else
                    {
                        objPlace.PIDPlaceLabel(locEndOfGroup, EndOfGroupPoint, LabeledItem: newRep, IsLeaderVisible: true); //第一条总的run就不需要了
                    }

                }
            }

            #endregion
            curConnectorRep = newRep;
            return newObjStockpile.Id;
        }
        /// <summary>
        /// 字符串中的数字
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string ExtractNumber(string input)
        {
            Match match = Regex.Match(input, @"-?\d+(.\d+)?");
            if (match.Success)
            {
                return match.Value;
            }
            else
            {
                return "";
            }
        }
        public static int GCD(int a, int b)
        {
            while (b != 0)
            {
                int temp = b;
                b = a % b;
                a = temp;
            }
            return a;
        }
        /// <summary>
        /// 小数转分数
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ConvertToFraction(double value)
        {
            const double epsilon = 0.0001;
            int wholeNumberPart = (int)value;
            double fractionalPart = value - wholeNumberPart;
            int numerator = (int)(fractionalPart / epsilon);
            int denominator = (int)(1 / epsilon);
            int gcd = GCD(numerator, denominator);

            numerator /= gcd;
            denominator /= gcd;

            if (wholeNumberPart != 0)
            {
                return $"{wholeNumberPart}-{numerator}/{denominator}";
            }
            else
            {
                return $"{numerator}/{denominator}";
            }
        }

        /// <summary>
        /// 往.PID上放symbol
        /// </summary>
        /// <param name="startX"></param>
        /// <param name="startY"></param>
        /// <param name="endX"></param>
        /// <param name="endY"></param>
        /// <returns>返回API创建的对象的symbol id</returns>
        string MyPlaceComp(double startX, double startY, RunOrientation orientation, string symFile, double Rotation = 0)
        {
            LMSymbol newObjStockpile;

            if (orientation == RunOrientation.horizontal)
            {
                newObjStockpile = objPlace.PIDPlaceSymbol(symFile, startX, startY, Rotation: Rotation);
                LlamaObjNeedCommit += 1;
                return newObjStockpile.Id;

            }
            else if (orientation == RunOrientation.vertical)
            {
                newObjStockpile = objPlace.PIDPlaceSymbol(symFile, startX, startY, Rotation: Math.PI / 2 + Rotation - 2 * Math.PI);  //暂时
                LlamaObjNeedCommit += 1;
                return newObjStockpile.Id;
            }
            return "error";

        }
    }
}
